package com.enjoychain.www.security.service.impl.report.policy;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.exception.RenderException;
import com.deepoove.poi.policy.RenderPolicy;
import com.deepoove.poi.render.compute.EnvModel;
import com.deepoove.poi.render.compute.RenderDataCompute;
import com.deepoove.poi.render.processor.DocumentProcessor;
import com.deepoove.poi.render.processor.EnvIterator;
import com.deepoove.poi.resolver.TemplateResolver;
import com.deepoove.poi.template.ElementTemplate;
import com.deepoove.poi.template.MetaTemplate;
import com.deepoove.poi.template.run.RunTemplate;
import com.deepoove.poi.util.ReflectionUtils;
import com.deepoove.poi.util.TableTools;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTVMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class OwnerLoopRowTableRenderPolicy implements RenderPolicy {
    private String prefix;
    private String suffix;
    private boolean onSameLine;

    public OwnerLoopRowTableRenderPolicy() {
        this(false);
    }

    public OwnerLoopRowTableRenderPolicy(boolean onSameLine) {
        this("[", "]", onSameLine);
    }

    public OwnerLoopRowTableRenderPolicy(String prefix, String suffix) {
        this(prefix, suffix, false);
    }

    public OwnerLoopRowTableRenderPolicy(String prefix, String suffix, boolean onSameLine) {
        this.prefix = prefix;
        this.suffix = suffix;
        this.onSameLine = onSameLine;
    }

    @Override
    public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
        RunTemplate runTemplate = (RunTemplate) eleTemplate;
        XWPFRun run = runTemplate.getRun();
        try {
            if (!TableTools.isInsideTable(run)) {
                throw new IllegalStateException(
                        "The template tag " + runTemplate.getSource() + " must be inside a table");
            }
            XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
            XWPFTable table = tagCell.getTableRow().getTable();
            run.setText("", 0);

            int templateRowIndex = getTemplateRowIndex(tagCell);
            if (null != data && data instanceof Iterable) {
                Iterator<?> iterator = ((Iterable<?>) data).iterator();
                List<XWPFTableRow> templateRows = new ArrayList<>();
                int beginIndex = templateRowIndex;
                for (int i=0;i<9;i++) {
                    templateRows.add(table.getRow(beginIndex));
                    beginIndex++ ;
                }
                int insertPosition = 0;

                TemplateResolver resolver = new TemplateResolver(template.getConfig().copy(prefix, suffix));
                boolean firstFlag = true;
                int index = 0;
                boolean hasNext = iterator.hasNext();
                while (hasNext) {
                    Object root = iterator.next();
                    hasNext = iterator.hasNext();
                    insertPosition = templateRowIndex;
                    templateRowIndex = templateRowIndex + 9;
                    List<XWPFTableRow> nextRows = new ArrayList<>();
                    int nextFlag = insertPosition;
                    for (int i=0;i<9;i++) {
                        table.insertNewTableRow(nextFlag++);
                    }
                    setTableRow(table, templateRows, insertPosition);

                    // double set row

                    for (int i=0;i<9;i++) {
                        XmlCursor newCursor = templateRows.get(i).getCtRow().newCursor();
                        newCursor.toPrevSibling();
                        XmlObject object = newCursor.getObject();
                        nextRows.add(new XWPFTableRow((CTRow) object, table));
                    }
                    if (!firstFlag) {
                        // update VMerge cells for non-first row
                        for (XWPFTableRow nextRow : nextRows) {
                            List<XWPFTableCell> tableCells = nextRow.getTableCells();
                            for (XWPFTableCell cell : tableCells) {
                                CTTcPr tcPr = TableTools.getTcPr(cell);
                                CTVMerge vMerge = tcPr.getVMerge();
                                if (null == vMerge) continue;
                                if (STMerge.RESTART == vMerge.getVal()) {
                                    vMerge.setVal(STMerge.CONTINUE);
                                }
                            }
                        }
                    } else {
                        firstFlag = false;
                    }
                    setTableRow(table, nextRows, insertPosition);
//                    for (XWPFTableRow nextRow: nextRows) {
//                        RenderDataCompute dataCompute = template.getConfig()
//                                .getRenderDataComputeFactory()
//                                .newCompute(EnvModel.of(root, EnvIterator.makeEnv(index++, hasNext)));
//                        List<XWPFTableCell> cells = nextRow.getTableCells();
//                        cells.forEach(cell -> {
//                            List<MetaTemplate> templates = resolver.resolveBodyElements(cell.getBodyElements());
//                            new DocumentProcessor(template, resolver, dataCompute).process(templates);
//                        });
//                    }
                }
            }
//            for(int i = 0; i< 9 ; i++) {
//                table.removeRow(templateRowIndex --);
//            }
            afterloop(table, data);
        } catch (Exception e) {
            throw new RenderException("HackLoopTable for " + eleTemplate + "error: " + e.getMessage(), e);
        }
    }

    private int getTemplateRowIndex(XWPFTableCell tagCell) {
        XWPFTableRow tagRow = tagCell.getTableRow();
        return onSameLine ? getRowIndex(tagRow) : (getRowIndex(tagRow) + 1);
    }

    protected void afterloop(XWPFTable table, Object data) {
    }

    @SuppressWarnings("unchecked")
    private void setTableRow(XWPFTable table, List<XWPFTableRow> templateRows, int pos) {
        List<XWPFTableRow> rows = (List<XWPFTableRow>) ReflectionUtils.getValue("tableRows", table);
        for (XWPFTableRow templateRow : templateRows) {
            rows.set(pos, templateRow);
            table.getCTTbl().setTrArray(pos, templateRow.getCtRow());
            pos++;
        }
    }

    private int getRowIndex(XWPFTableRow row) {
        List<XWPFTableRow> rows = row.getTable().getRows();
        return rows.indexOf(row);
    }
}
